package steed.filter;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Attribute;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Node;
import org.jsoup.safety.Whitelist;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;

import steed.util.base.PropertyUtil;
import steed.util.base.StringUtil;
/**
 * xss攻击过滤器，全局解决xss问题
 * 
 * @author 战马
 *
 */
public class XSSFilter implements Filter {
	/**
	 * 允许
	 */
	protected static String[] allowedSpecialCharacters;
	protected static String[] notFilt;
	private static final String[] strNotArrow = {"^[\\s\\S]*expression([\\s\\S]*)[\\s\\S]*$","^[\\s\\S]*javascript:[\\s\\S]*$"};		
	
	@Override
	public void destroy() {
		
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		HttpServletRequest request2 = (HttpServletRequest)request;
		CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver();
//		BaseUtil.out(commonsMultipartResolver.isMultipart(request2));
		if (commonsMultipartResolver.isMultipart(request2)) {
			request2 = commonsMultipartResolver.resolveMultipart(request2);
		}
		chain.doFilter(new XSSRequestWrapper((HttpServletRequest)request), response);
	}

	@Override
	public void init(FilterConfig config) throws ServletException {
		String initParameter = config.getInitParameter("allowedSpecialCharacters");
		if (StringUtil.isStringEmpty(initParameter)) {
			allowedSpecialCharacters = new String[]{};
		}else {
			allowedSpecialCharacters = initParameter.split(",");
		}
		initParameter = config.getInitParameter("notFilt");
		if (StringUtil.isStringEmpty(initParameter)) {
			notFilt = new String[]{};
		}else {
			notFilt = initParameter.split(",");
		}
	}
	
	/**
	 * 解决URL带中文参数时request.getParameter(name)乱码问题
	 * @author battle_steed
	 */
	private class XSSRequestWrapper extends HttpServletRequestWrapper{
		private boolean initedMap = false;
		private Map<String, String[]> map = new HashMap<String, String[]>();
		public XSSRequestWrapper(HttpServletRequest request) {
			super(request);
		}
		

		@Override
		public Map<String, String[]> getParameterMap() {
			if (initedMap) {
				return map;
			}
			Map<String, String[]> tempMap = super.getParameterMap();
			for (String key:tempMap.keySet()) {
				if (map.containsKey(key)) {
					continue;
				}
				enCode(tempMap.get(key), key);
			}
			initedMap = true;
			return map;
		}
		

		@Override
		public String getParameter(String name) {
			String[] temp = getParameterValues(name);
			if (temp == null || temp.length < 1) {
				return null;
			}
			return temp[0];
		}

		private String[] enCode(String[] param,String name){
			for (String temp:notFilt) {
				if (temp.equals(name)) {
					map.put(name, param);
					return param;
				}
			}
			
			boolean allowSpecialCharacter = false;
			for (String temp:allowedSpecialCharacters) {
				if (temp.equals(name)) {
					allowSpecialCharacter = true;
					break;
				}
			}
			for (int i = 0; i < param.length; i++) {
				param[i] = enCode(param[i], allowSpecialCharacter);
			}
			
			
			map.put(name, param);
			return param;
		}
		
		/**
		 * 清除富文本内容中的跨站脚本攻击字符,
		 * 如果不是富文本请用trensferrSpecailCharacter
		 * @param str
		 * @return
		 */
		private String cleanXss(String str) {
			Whitelist relaxed = Whitelist.relaxed();
			relaxed.addAttributes(":all", "style");
			relaxed.addAttributes(":all", "class");
			relaxed.addTags("meta");
			relaxed.addAttributes("meta", "name");
			relaxed.addAttributes("meta", "content");
//			relaxed.addAttributes("iframe", "src");
//			relaxed.addAttributes("iframe", "height");
//			relaxed.addAttributes("iframe", "width");
//			relaxed.addTags("iframe");
			String clean = Jsoup.clean(str, PropertyUtil.getConfig("site.rootURL"),relaxed);
			Document doc = Jsoup.parse(clean);
			
			validateNode(doc);
			return doc.body().html();
		}
		
		private void validateNode(Node n){
			for (Node node:n.childNodes()) {
				for(Attribute a:node.attributes().asList()){
					String value = a.getValue();
					for (String str:strNotArrow) {
						if (value.matches(str)) {
							node.removeAttr(a.getKey());
							break;
						}
					}
				}
				validateNode(node);
			}
		}
		
		
		private String enCode(String target,boolean allowSpecialCharacter){
			if (StringUtil.isStringEmpty(target)) {
				return target;
			}
			if (allowSpecialCharacter) {
				return cleanXss(target);
			}else {
				return StringUtil.transferrCharacter((target).replace("'", "＇"));
			}
		}
		

		@Override
		public String[] getParameterValues(String name) {
			String[] strings = map.get(name);
			if (strings == null) {
				strings = super.getParameterValues(name);
				if (strings == null) {
					return strings;
				}
				enCode(strings,name);
			}
			return strings;
		}
	}

}
